我想了解一些装配.
汇编如下,我对该testl
行感兴趣:
000319df 8b4508 movl 0x08(%ebp), %eax 000319e2 8b4004 movl 0x04(%eax), %eax 000319e5 85c0 testl %eax, %eax 000319e7 7407 je 0x000319f0
我想了解的那点testl
之间的%eax
和%eax
?我认为这段代码的具体内容并不重要,我只是试图用自己来理解测试 - 这种价值总是不正确吗?
意思test
是将参数和AND组合在一起,并将结果检查为零.因此,此代码测试EAX是否为零.je
如果为零,将跳跃.
顺便说一句,这会生成一个比cmp eax, 0
编译器通常这样做的原因更小的指令.
它测试eax
是0,或更高,或更低.在这种情况下,如果eax
为0 则跳转.
测试指令在操作数之间执行逻辑AND操作,但不将结果写回寄存器.只更新标志.
在您的示例中,测试eax,如果eax为零,则eax将设置零标志,如果最高位设置则设置符号标志以及其他一些标志.
如果设置了零标志,则跳转如果等于(je)指令跳转.
您可以将代码转换为更易读的代码,如下所示:
cmp eax, 0 je somewhere
它具有相同的功能,但需要一些字节更多的代码空间.这就是编译器发出测试而不是比较的原因.
test
就像and
,除了它只写FLAGS,保持其输入不被修改.使用两个不同的输入,它可用于测试某些位是否全为零,或者是否设置了至少一个位.(例如,test al, 3
如果EAX是4的倍数,则设置ZF(因此将其低2位归零).
test eax,eax
将所有的标志正是以同样的方式cmp eax, 0
将:
CF和OF清除(AND/TEST总是那样;减去零从不产生进位)
根据EAX中的值,ZF,SF和PF.(a = a&a = a-0
)
(除了过时的AF(辅助进位标志,由ASCII/BCD指令使用).TEST保留 未定义,但CMP"根据结果"设置它.由于减零不能产生从第4到第5的进位位,CMP应始终清除AF).
TEST更小(没有立即),有时更快(在比CMP更多的情况下,可以在更多CPU上宏 - 融合到比较和分支uop). 这使得test
用于测试寄存器的优选习惯用于零.
使用CMP与即时0唯一的常见的原因是,当你想比较对内存操作数(例如,cmp reg,0
在一个隐含的长度C风格字符串的结尾来检查结束零字节).
AVX512F增加了cmpb $0, (%esi)
AVX512DQ/BW(Skylake但不是KNL)添加kortestw k1, k2
,它在AVX512掩码寄存器(k0..k7)上运行,但仍然设置常规FLAGS,就像ktestb/w/d/q k1, k2
整数test
或OR
指令一样.
AND
是基于AVX512比较结果分支/ cmovcc/setcc的惯用方法,取代了SSE/AVX2 ptest
+ ucomiss
或kortestw k1,k1
.
使用(v)pmovmskb/ps/pd
vs. test
可能会令人困惑.
cmp
并且jz
字面上是相同的指令,即机器代码中的相同操作码. 他们做同样的事情,但对人类有不同的语义.反汇编程序(通常是编译器的asm输出)只会使用一个,因此语义上的区别就会丢失.
je
并jz
在两个输入相等时设置ZF(即减法结果为0). je
(如果相等则跳转)是语义相关的同义词.
cmp
/ sub
当结果为零时再次设置ZF,但没有"相等"测试.测试后的ZF没有告诉你两个操作数是否相等.所以je
(如果为零则跳转)是语义相关的同义词.
这段代码来自一个子程序,它被赋予一个指向某事物的指针,可能是一些结构或对象.第二行取消引用指针,从该东西获取一个值 - 可能本身就是一个指针,或者只是一个int,存储为第二个成员(偏移+4).第3行和第4行将此值测试为零(如果它是指针则为NULL),如果为零,则跳过以下几个操作(未显示).
对零的测试有时被编码为与直接的文字零值进行比较,但编写此文件的编译器(或人?)可能认为测试操作会运行得更快 - 考虑所有现代CPU的东西,如流水线和寄存器重命名.它来自同一个技巧包含清除XOR EAX,EAX(我在科罗拉多州某人的车牌上看到的)登记册的想法,而不是明显但可能更慢的MOV EAX,#0(我使用较旧的表示法) ).
在asm中,像perl,TMTOWTDI.